//
// Created on 2024/4/22.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#include "glesshader.h"
#include "mlog.h"
static std::map<std::string, gles_shader *> shaders;
static std::map<std::string, gles_program *> programs;

gles_shader *gles_shadermgr::CreateShader(shaderType type, std::string name) {
    std::string key = name + (type == shaderType::FragmentShader ? "_fs" : "_vs");
    auto itfind = shaders.find(key);
    if (itfind != shaders.end()) {
        return itfind->second;
    }

    gles_shader *shader = new gles_shader();
    int len = name.length();
    if (len > 32)
        len = 32;

    memcpy(shader->name, name.c_str(), len);
    shader->name[len + 1] = 0;
    shader->type = type;
    shader->build = false;
    shader->handle = glCreateShader(type == shaderType::FragmentShader ? GL_FRAGMENT_SHADER : GL_VERTEX_SHADER);
    shaders[key] = shader;
    return shader;
}
gles_shader *gles_shadermgr::GetShader(shaderType type, std::string name) {
    std::string key = name + (type == shaderType::FragmentShader ? "_fs" : "_vs");
    auto itfind = shaders.find(key);
    if (itfind != shaders.end()) {
        return itfind->second;
    }
    return nullptr;
}

bool gles_shadermgr::SourceShader(gles_shader *shader, std::string code) {
    if (shader->build)
        return true;
    const char *codeptr = code.c_str();
    int codelen = code.length();
    glShaderSource(shader->handle, 1, &codeptr, &codelen);
    glCompileShader(shader->handle);
    int status;
    glGetShaderiv(shader->handle, GL_COMPILE_STATUS, &status);
    if (status == GL_TRUE) {
        shader->build=true;
        return true;
    }
    return false;
}
std::string gles_shadermgr::GetShaderInfo(gles_shader *shader) {
    char buf[1025];
    int len;
    glGetShaderInfoLog(shader->handle, 1024, &len, buf);
    return buf;
}


gles_program *gles_shadermgr::CreateProgram(std::string name, gles_shader *vs, gles_shader *fs) { std::string key = name;
    auto itfind = programs.find(key);
    if (itfind != programs.end()) {
        return itfind->second;
    }
    gles_program* prog=new gles_program();
    
    int len = name.length();
    if (len > 32)
        len = 32;
    memcpy(prog->name, name.c_str(), len);
    prog->name[len + 1] = 0;
    prog->vs=vs;
    prog->fs=fs;
    prog->handle = glCreateProgram();
    prog->build=false;
   
    //glUseProgram(prog->handle);
    glAttachShader(prog->handle,vs->handle);
    glAttachShader(prog->handle,fs->handle);
    glLinkProgram(prog->handle);
    int status;
    glGetProgramiv(prog->handle,GL_LINK_STATUS ,&status);
    if (status == GL_TRUE) {
        prog->build = true;

    } else {
        MLOGFormat("Shader Link Error. stat= %{public}d",status);
    }

    programs[key] = prog;
    return prog;
}
gles_program *gles_shadermgr::GetPrograme(std::string name) {
    std::string key = name;
    auto itfind = programs.find(key);
    if (itfind != programs.end()) {
        return itfind->second;
    }
    return nullptr;
}   
std::string gles_shadermgr::GetProgramInfo(gles_program *prog)
{
    char buf[1025];
    int len;
    glGetProgramInfoLog(prog->handle, 1024, &len, buf);
    return buf;
}
const char *vs = {"#version 300 es\n" 
"layout(location = 0) in vec3 position;\n"
"layout(location = 2) in vec4 color;\n"

"out vec4 vertexColor;\n"

"void main() {\n"
"    gl_Position = vec4(position, 1.0);\n"
"    vertexColor = color;\n"
"}\n"
};
const char *fs = R"(#version 300 es 
precision mediump float;
out vec4 FragColor;

in vec4 vertexColor;

void main() { FragColor = vertexColor; }
)";

    void
    gles_shadermgr::Init() {
    auto vsshader= CreateShader(shaderType::VertexShader,"def_vs");
    auto fsshader =CreateShader(shaderType::FragmentShader,"def_fs");
    
    SourceShader(vsshader,vs);
    auto vsinfo =GetShaderInfo(vsshader);
    MLOGFormat("vs %{public}d  %{public}s", vsshader->build,                vsinfo.c_str());
    
    SourceShader(fsshader, fs);
    auto fsinfo = GetShaderInfo(fsshader);
    MLOGFormat("fs %{public}d  %{public}s", fsshader->build, fsinfo.c_str());
    
    auto prog = CreateProgram("def",vsshader,fsshader);
    auto info =GetProgramInfo(prog);
    MLOGFormat("prog %{public}d %{public}s", prog->build       ,info.c_str());
}
